﻿using System;
using System.Collections.Generic;
using System.Linq;
using System.Text;
using DSS_XNA.Engine.Error;
using DSS_XNA.Engine.Util;
using System.IO;
using DSS_XNA;
using DSS_XNA.Engine.Script.Functions;
using DSS_XNA.Engine.Script.Variables;

namespace DSS_XNA.Engine.Script
{
    public class ScriptManager
    {
        #region Properties

        public const string DefaultScriptFileName = "scripts\\main.spt";

        public Script CurrentScript
        {
            get { return _currentScript; }
            set { _currentScript = value; }
        }
        private Script _currentScript = null;

        public Game1 Engine
        {
            get { return _engine; }
            set { _engine = value; }
        }
        private Game1 _engine = null;

        public string LoadScriptRequestPath
        {
            get { return _loadScriptRequestPath; }
            set { _loadScriptRequestPath = value; }
        }
        private string _loadScriptRequestPath = String.Empty;

        public Dictionary<string, IVariable> GlobalVariables
        {
            get { return _globalVariables; }
            set { _globalVariables = value; }
        }
        private Dictionary<string, IVariable> _globalVariables = new Dictionary<string, IVariable>();

        #endregion

        #region Public Methods

        public ScriptManager()
        {
        }

        public bool Init(Game1 engine)
        {
            if (engine == null)
                throw new DSSException("'engine' is null.");

            this.Engine = engine;

            this.LoadScript(DefaultScriptFileName);

            return true;
        }

        public void LoadScriptRequest(string filePath)
        {
            this.LoadScriptRequestPath = filePath;
        }

        public void RunCommandRequest(string command)
        {
            ScriptCommand newCommand = new ScriptCommand();
            newCommand.Load(command);
            this.RunCommandRequest(newCommand);
        }

        public void RunCommandRequest(ScriptCommand command)
        {
            if (this.CurrentScript == null)
            {
                // maybe a hack, but as there is no script, just run it here
                command.Run(this, this.Engine);
            }
            else
            {
                this.CurrentScript.ExtraCommands.Add(command);
            }
        }

        public void SetNoScript()
        {
            this.CurrentScript = null;
        }

        public bool Run(Game1 engine)
        {
            if (engine == null)
                throw new DSSException("'engine' is null.");

            if (!String.IsNullOrEmpty(this.LoadScriptRequestPath))
            {
                this.LoadScript(this.LoadScriptRequestPath);
                this.LoadScriptRequestPath = String.Empty;
            }

            if(this.CurrentScript != null)
                this.CurrentScript.Run(engine);

            return true;
        }

        public object GetParameterValue(string value)
        {
            if (value == null)
                throw new DSSException("'value' is null.");
            if (value.Length == 0)
                throw new DSSException("'value' is zero-length.");

            value = value.Trim();

            object o = this.VariableValue(value);
            if (o != null)
                return o;

            // is this value a function?
            string[] values = value.Split(',');

            // is the value a pre-defined function?
            if (ScriptCommands.AvaliableBuildInFunctions.ContainsKey(values[0].ToLower()))
            {
                ScriptCommand.ScriptBuildInFunctionActionDelegate func;
                func = ScriptCommands.AvaliableBuildInFunctions[values[0].ToLower()];

                List<string> parameters = new List<string>();
                for (int i = 1; i < values.Length; i++)
                    parameters.Add(values[i]);

                return func(parameters, this, this.Engine);
            }

            // a user function?
            // TBI...

            // not a known function or a normal value
            return values[0];
        }

        public void Shutdown()
        {
            this.CurrentScript = null;
            this.GlobalVariables.Clear();
        }

        public void AddGlobalVariable(IVariable var)
        {
            if (var == null)
                throw new DSSException("'var' is null.");

            if (this.GlobalVariables.ContainsKey(var.Name))
                throw new DSSException(String.Format("There is already a global variable with the name '{0}'",
                    var.Name));

            this.GlobalVariables.Add(var.Name, var);
        }

        public void AddLocalVariable(IVariable var)
        {
            if (var == null)
                throw new DSSException("'var' is null.");

            if(this.CurrentScript == null)
                throw new DSSException(String.Format("No script to add local variable '{0}' too",
                    var.Name));

            this.CurrentScript.AddVariable(var);
        }

        public IVariable GetVariable(string name)
        {
            if (name == null)
                throw new DSSException("'name' is null.");
            if (name.Length == 0)
                throw new DSSException("'name' is zero-length.");

            name = name.ToLower().Trim();

            IVariable var = null;

            if (this.GlobalVariables.ContainsKey(name))
            {
                var = this.GlobalVariables[name];
            }
            else if (this.CurrentScript != null && this.CurrentScript.Variables.ContainsKey(name))
            {
                var = this.CurrentScript.Variables[name];
            }

            return var;
        }

        public void UpdateVariableValue(string name, object value)
        {
            if (name == null)
                throw new DSSException("'name' is null.");
            if (name.Length == 0)
                throw new DSSException("'name' is zero-length.");

            name = name.ToLower().Trim();

            if (this.GlobalVariables.ContainsKey(name))
            {
                this.GlobalVariables[name].Value = value;
            }
            else if (this.CurrentScript != null && this.CurrentScript.Variables.ContainsKey(name))
            {
                this.CurrentScript.Variables[name].Value = value;
            }
        }

        #endregion

        #region Private Methods

        private bool LoadScript(string filePath)
        {
            filePath = Util.Util.GetFullPath(filePath);
            this.CurrentScript = new Script(this);
            return this.CurrentScript.Load(filePath);
        }

        private object VariableValue(string value)
        {
            value = value.ToLower().Trim();

            object o = null;

            if (this.GlobalVariables.ContainsKey(value))
            {
                o = this.GlobalVariables[value].Value;
            }
            else if (this.CurrentScript != null && this.CurrentScript.Variables.ContainsKey(value))
            {
                o = this.CurrentScript.Variables[value].Value;
            }

            return o;
        }

        #endregion
    }
}
